//通过类的方式来进行封装，会让代码更具有复用性 
// 也可以方便添加新的属性和方法
export default class Request{
	// 定义实例属性，用来设置默认请求参数
	defaults = {
		baseUrl:'',
		url:'',
		data:null,
		method:'GET',
		header:{
			'Content-type':'application/json', //用来设置数据的交互格式
		},
		timeout:60000, //超时时长，默认超时时长是1秒钟
		isLoading:true,//是否使用默认的loading
	}
	// 用于创建和初始化属性以及方法
	constructor(params={}){
		this.defaults = Object.assign({},this.defaults,params)
	}
	// 拦截器
	interceptor={
		// 请求拦截器
		request:(config)=>config,
		// 响应拦截器
		response:(response)=>response
	}
	// 定义数组队列
	// 初始值需要是一个空数组，用来存储请求队列，存储请求标识
	queue = []
	timerId = null
	/**
	 * @description  请求方法
	 * @param {Object} options
	 */
	requset(options){
		options.url=this.defaults.baseUrl + options.url
		options = {...this.defaults,...options}
		this.timerId && clearTimeout(this.timerId)
		if(options.isLoading&&options.method!=='UPLOAD'){
			// 如果队列是空数组说明将要开始发送请求，显示加载图标
			if(this.queue.length===0){
				uni.showLoading()
			}
			// 每一个请求会给队列添加请求标识-request
			this.queue.push('request')
		}		
		// 在请求之前调用请求拦截器，比如用来设置公共属性
		this.interceptor.request(options)
			// console.log(options);
			// 需要使用promise封装请求，处理异步请求
		return new Promise((resolve,reject)=>{
			if(options.method!=='UPLOAD'){
				uni.request({
					...options,
					// 不管是请求失败还是请求成功，都已经将响应的数据传递给了响应拦截器
					// 这时候合并参数的时候，追加一个属性 isSuccess
					
					// 请求成功时触发，请求错误也会触发，除非根本就没有请求到服务器				
					success:(res)=>{
						// 无论请求成功还是请求失败都必须调用响应拦截器，相应拦截器可以简响应的数据
						// 再给响应拦截器传递参数时，要把请求参数也传进去方便调试
						resolve(this.interceptor.response(Object.assign({},res,{config:options,isSuccess:true})))
					},
					// 请求失败时触发
					fail:(err)=>{
						// 无论请求成功还是请求失败都必须调用响应拦截器，相应拦截器可以简响应的数据
						// 再给响应拦截器传递参数时，要把请求参数也传进去方便调试
						reject(this.interceptor.response(Object.assign({},err,{config:options,isSuccess:false})))
					},
					complete:()=>{	
						if(options.isLoading){
							// 通过使用定时器来解决A请求依赖B请求导致的加载动画闪烁问题。
							this.queue.pop()
							this.queue.length === 0 && this.queue.push('request')
							this.timerId = setTimeout(()=>{
								// 每完成一个请求就删除一个请求标识
								this.queue.pop()
								// 当请求标识为0个时隐藏加载提示框。
								if(this.queue.length===0){
									uni.hideLoading()
								}
							},1)
						}					
					} 
				})
			}else{
				uni.uploadFile({
					...options,
					//上传成功的回调函数
					success: (res) => {
						// 需要将服务器返回的JSON字符串通过JSON.parse转成对象
						res.data = JSON.parse(res.data)
						// 合并参数
						resolve(this.interceptor.response(Object.assign({},res,{config:options,isSuccess:true})))					
					},
					fail: (err) => {//失败的回调
						reject(this.interceptor.response(Object.assign({},err,{config:options,isSuccess:true})))	
					}
				})
			}									
		})
	}
	// 将请求进一步细分，方便业务处理
	get(url,data={},config={}){
		// console.log(url,data,config);
		return this.requset(Object.assign({url,data,method:'GET'},config))
	}
	delete(url,data={},config={}){
		return this.requset(Object.assign({url,data,method:'DELETE'},config))
	}
	post(url,data={},config={}){
		return this.requset(Object.assign({url,data,method:'POST'},config))
	}
	put(url,data={},config={}){
		return this.requset(Object.assign({url,data,method:'PUT'},config))
	}
	// 用来处理并发请求
	all(...promise){
		return Promise.all(promise)
	}
	
	/**
	 * @description upload实例方法，对uni.uploadFile进行封装
	 * @param {*} url 文件上传地址、接口地址
	 * @param {*} filePath 要上传的文件资源路径
	 * @param {string} [name='file']  文件对应的key
	 * @param {{}} [config={}] 其他配置项
	 */
	upload(url,filePath,name='file',config={}){
		return this.requset(Object.assign({url,filePath,name,method:'UPLOAD'},config))
	}
}